package org.web.common.sso.client.biz.impl;

import com.google.gson.Gson;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.validator.routines.InetAddressValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.web.base.domain.ResultDO;
import org.web.base.helper.HttpRequestHelper;
import org.web.base.helper.filter.URIHelper;
import org.web.common.sso.client.domain.SessionAccountDO;
import org.web.common.sso.client.helper.SSOHelper;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;

@Setter
@Getter
public abstract class SSOAbstractFilter implements Filter, PropertiesConstantInterface {

    Logger logger = LoggerFactory.getLogger(SSOAbstractFilter.class);

    private SSOAbstractFilterConfig config = null;

    @SneakyThrows
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String uri = request.getRequestURI();

        // 第一个场景，判断是否可是过滤免登陆URI，忽略大小写。
        String ignoreUri = config.getIgnoreURIList().stream().filter(str -> StringUtils.startsWithIgnoreCase(uri, str)).findAny().orElse(null);
        if (StringUtils.isNoneBlank(ignoreUri)) {
            filterChain.doFilter(request, response);
            return;
        }

        // 第二个场景，基于exchangeToken获取用户信息,理论只生效一次。
        String exchangeToken = request.getParameter(PARAMETER_SSO_EXCHANGE_TOKEN);
        if (StringUtils.isNotBlank(exchangeToken)) {
            ResultDO<SessionAccountDO> resultDO = getSessionBySSOExchangeToken(exchangeToken, getConfig().getCookieRule());
            if (resultDO.isSuccess()) {
                if (config.getIsCookieWay()) {
                    Cookie cookie = new Cookie(PROPERTIES_TOKEN_NAME_DEFAULT_VALUE, resultDO.getDomain().getSsoToken());
                    String serverName = request.getServerName();
                    String urlString = request.getRequestURL().toString();
                    URI requestURI = new URI(urlString);
                    String queryString = URIHelper.removeParamToURL(request.getQueryString(), PARAMETER_SSO_EXCHANGE_TOKEN, exchangeToken);
                    String redirectURL = urlString + "?" + queryString;
                    if (InetAddressValidator.getInstance().isValid(requestURI.getHost())) {
                        // 如果访问请求是IP。
                        if (config.getLocalIPList().contains(request.getRemoteAddr())) {
                            cookie.setDomain("localhost");
                            redirectURL = redirectURL.replaceFirst(serverName, "localhost");
                        } else {
                            // 用于保护
                            response.getWriter().println("TODO , please use domain instead of IP.");
                            response.flushBuffer();
                            return;
                        }
                    }
                    cookie.setMaxAge(config.getCookieRule().getCookieMaxAge());
                    response.addCookie(cookie);

                    // 重定向本域原始URL，目标是为了隐藏exchangeToken.
                    response.sendRedirect(redirectURL);
                } else {
                    SSOHelper.setSessionAccountDO(resultDO.getDomain());
                    filterChain.doFilter(request, response);
                }
            } else {
                sendRedirect(response);
            }
            return;
        }

        // 第三个场景，从cookie或者request中获取token，如果token为空。
        String token = HttpRequestHelper.getValueByParamOrCookie(request, config.getParamToken());
        if (StringUtils.isNotEmpty(token)) {
            ResultDO<SessionAccountDO> getSessionByTokenResultDO = getSessionByToken(token);
            if (getSessionByTokenResultDO.isSuccess()) {
                SSOHelper.setSessionAccountDO(getSessionByTokenResultDO.getDomain());
                filterChain.doFilter(request, response);
            } else {
                sendRedirect(response);
            }
            return;
        }
        sendRedirect(response);
    }


    @Override
    public void init(FilterConfig filterConfig) {
        config = new SSOAbstractFilterConfig();
        SSOAbstractFilterConfig.init(config);
        logger.info(config.toString());
    }

    /**
     * 基于token和request 获取 session数据。
     */
    abstract ResultDO<SessionAccountDO> getSessionByToken(String token);

    /**
     * 基于exchangeSSOToken 获取 session数据。
     */
    abstract ResultDO<SessionAccountDO> getSessionBySSOExchangeToken(String ssoExchange, CookieRule cookieRule);

    /**
     * It's probably for you extends this method. It gives us a default method. 处理token 失败的重定向。
     */
    abstract void sendRedirect(HttpServletResponse response) throws IOException;

    abstract void executeWhenError(HttpServletResponse response) throws IOException;

}


